#include "backtrace.h"
#include <stdlib.h>
#include <execinfo.h>

namespace nsi {
namespace spike {

backtrace::backtrace()
  : depth_(0),
    max_depth_(default_depth),
    addresses_(0),
    symbols_(0)
{

}

backtrace::backtrace(int max_depth)
  : depth_(0),
    max_depth_(default_depth),
    addresses_(0),
    symbols_(0)
{

}

backtrace::~backtrace()
{
  destroy();
}

bool backtrace::create()
{
  if (create_addresses())
    return create_symbols();
  return false;
}

bool backtrace::create(const backtrace::address_type signal)
{
  if (!create_addresses())
    return false;

  if (depth_ > 1)
    addresses_[1] = signal;

  return create_symbols();
}

bool backtrace::create_addresses()
{
  if (addresses_ != 0)
    destroy_addresses();

  addresses_ = new address_type[max_depth_];
  depth_ = ::backtrace(addresses_, max_depth_);

  return depth_ > 0;
}

bool backtrace::create_symbols()
{
  if (symbols_ != 0)
    destroy_symbols();

  symbols_ = ::backtrace_symbols(addresses_, depth_);

  return symbols_ != 0;
}

void backtrace::destroy()
{
  destroy_addresses();
  destroy_symbols();
}

void backtrace::destroy_addresses()
{
  // Free addresses 
  if (addresses_ != 0)
  {
    delete [] addresses_;
    addresses_ = 0;
  }
}

void backtrace::destroy_symbols()
{
  // Free symbols
  if (symbols_ != 0)
  {
    free(symbols_);
    symbols_ = 0;
  }
}

void backtrace::print() const
{
  print(stderr);
}

void backtrace::print(FILE* file) const
{
  if (!created())
    return;

  for (int i = 0; i < depth(); ++i)
    fprintf(file, "%d: %s\n", i, symbol(i));
}

} // namespace spike
} // namespace nsi
